/*
 * @Author: intellectual-seeker 3336339701@qq.com
 * @Date: 2025-04-11 11:50:10
 * @LastEditors: intellectual-seeker 3336339701@qq.com
 * @LastEditTime: 2025-08-07 20:15:08
 * @FilePath: /code/lesson25/test2/processpool.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */


//processpool 创建子进程 控制子进程 回收子进程
#include "task.hpp"

using namespace std;
//string在命名空间std中

//匿名枚举类型
//定义用来标识各种错误情况
enum
{
    UsageErr = 1,//使用手册问题
    ArgErr,
    PipeErr
};

//程序提醒使用手册
void Usage(const string& proc)
{
    //当前程序名称：proc
    std::cout << "Usage: " << proc << "subprocess-num" << std::endl;
}

//定义各个类用来描述管道
//匿名管道文件的写端文件fd (通过管道控制子进程)
//通过管道进行通信的子进程pid
//管道名称
class Channel
{
public:
    Channel(int wfd, pid_t subprocess_id, string name)
        :_wfd(wfd)
        ,_subprocess_id(subprocess_id)
        ,_name(name)
    {}

    int wfd()
    {
        return _wfd;
    }

    pid_t id()
    {
        return _subprocess_id;
    }

    string name()
    {
        return _name;
    }

    ~Channel()
    {}

private:
    int _wfd;
    pid_t _subprocess_id;
    string _name;
};

//定义一个类用来描述进程池
//描述管理与多个子进程通信的管道 和 子进程数量
class ProcessPool
{
public:
    ProcessPool(int sub_process_num)
        :_sub_process_num(sub_process_num)
    {}

    int CreateProcess(work_t work)
    {
        for(int i = 0; i < _sub_process_num; i++)
        {
            int pipefd[2];
            int n = pipe(pipefd);
            if(n < 0) return PipeErr;//检测管道创建是否成功

            pid_t fd = fork();
            //子进程(读)
            if(fd == 0)
            {
                close(pipefd[1]);//pipefd[0]: 读 pipefd[1]: 写
                dup2(pipefd[0], 0);//dup2(oldfd, newfd);

                work(getpid());

                //sleep(1);

                exit(0);
            }

            //父进程(写)
            //创建完成后退出
            close(pipefd[0]);

            string name = "channel-" + std::to_string(i);
            channels.push_back(Channel(pipefd[1], fd, name));
        }

        return 0;
    }

    //关闭子进程和相对应的管道文件
    //当管道文件写端关闭后，读端将管道内数据读取完毕返回0
    //work函数中处理，read接收到0后子进程退出，而后KillAll回收
    void KillAll()
    {
        //创建进程时的隐藏bug
        for(int i = channels.size() - 1; i >= 0; i--)
        {
            close(channels[i].wfd());
            waitpid(channels[i].id(), nullptr, 0);  
        }

        // for(int i = 0; i < channels.size(); i++)
        // {
        //     close(channels[i].wfd());
        //     waitpid(channels[i].id(), nullptr, 0);
        // }
    }

    int NextChannel()
    {
        static int pos = 0;
        int next = pos++;
        pos %= _sub_process_num;

        return next;
    }

    //在processpool类型中定义发送任务码的函数接口
    void SendTaskCode(int channel, size_t commandcode)
    {
        //显示出是哪个进程通过哪个管道发送的几号任务码
        pid_t fd = channels[channel].wfd();
        //fd为父进程的写端fd
        std::cout << "subprocess_id is " << channels[channel].id() << " "
        << channels[channel].name() << " fd is: " << fd 
        << " send commandcode is: " << commandcode << std::endl;
        write(fd, &commandcode, sizeof(commandcode));

        sleep(1);
    }

    ~ProcessPool()
    {}
private:
    vector<Channel> channels;//自创建进程与相对应通信管道
    int _sub_process_num;
};

//控制子进程
//rs轮询分配任务
void CtrlSubProcess(ProcessPool* processpool_ptr, int cnt)
{
    while(cnt--)
    {
        int channel = processpool_ptr->NextChannel();
        processpool_ptr->SendTaskCode(channel, NextTask());
    }
}

//命令行参数，获取指令
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return UsageErr;
    }

    //监测创建的进程个数
    int process_num = std::stoi(argv[1]);
    if(process_num <= 0) return ArgErr;

    //创建子进程
    ProcessPool* processpool = new ProcessPool(process_num);
    processpool->CreateProcess(work);

    //控制子进程执行任务
    srand((size_t)time(nullptr));
    CtrlSubProcess(processpool, 5);

    //回收子进程
    processpool->KillAll();

    std::cout << "quit now..." << std::endl;

    delete processpool;

    return 0;
}
//打印信息时间停止间隔？